#include <QPen>
#include <QPainter>
#include "surfaceimpl.h"
#include "../drawingsurface.h"
#include "../global.h"

namespace ShUi
{

class SurfaceImpl : public Impl<Surface>
{
public:
    DrawingSurface* _surface;
    QPixmap *_pixmap;
    QPen _pen;
    SsWireAt _at;
};

Surface::Surface(DrawingSurface* surface) : ShCo::Surface()
{
    SH_I(Surface);
    d->_surface = surface;
    d->_pixmap = new QPixmap(5, 5);
    d->_pixmap->fill(Qt::transparent);
    d->_pen.setColor(VgPenColor);
    d->_pen.setWidth(VgPenWidth);
    d->_pen.setCapStyle(Qt::RoundCap);
    d->_pen.setJoinStyle(Qt::RoundJoin);
    d->_at.lineStyle = S_LINE_SOLID;
    d->_at.capStyle = S_CAP_ROUND;
    d->_at.joinStyle = S_JOIN_ROUND;
    d->_at.color.r = VgPenColor.red();
    d->_at.color.g = VgPenColor.green();
    d->_at.color.b = VgPenColor.blue();
    d->_at.color.a = VgPenColor.alpha();
    d->_at.width = VgPenWidth;
}

Surface::~Surface()
{
    SH_E(Surface);
    SH_SAFE_DELETE(d->_pixmap);
}

void Surface::setAttribute(const SsWireAt* attrib)
{
    SH_D(Surface);
    if (!attrib)
        return;
    if (attrib->lineStyle != d->_at.lineStyle)
    {
        Qt::PenStyle style;
        switch (attrib->lineStyle)
        {
            case S_LINE_NO: style = Qt::NoPen; break;
            case S_LINE_SOLID: style = Qt::SolidLine; break;
            case S_LINE_DASH: style = Qt::DashLine; break;
            case S_LINE_DOT: style = Qt::DotLine; break;
            case S_LINE_DASH_DOT: style = Qt::DashDotLine; break;
            case S_LINE_DASH_DOT_DOT: style = Qt::DashDotDotLine; break;
            default: style = Qt::SolidLine;
        }
        d->_pen.setStyle(style);
        d->_at.lineStyle = attrib->lineStyle;
    }
    if (attrib->capStyle != d->_at.capStyle)
    {
        Qt::PenCapStyle capStyle;
        switch (attrib->capStyle)
        {
            case S_CAP_FLAT: capStyle = Qt::FlatCap; break;
            case S_CAP_ROUND: capStyle = Qt::RoundCap; break;
            case S_CAP_SQUARE: capStyle = Qt::SquareCap; break;
            default: capStyle = Qt::RoundCap;
        }
        d->_pen.setCapStyle(capStyle);
        d->_at.capStyle = attrib->capStyle;
    }
    if (attrib->joinStyle != d->_at.joinStyle)
    {
        Qt::PenJoinStyle joinStyle;
        switch (attrib->joinStyle)
        {
            case S_JOIN_BEVEL: joinStyle = Qt::BevelJoin; break;
            case S_JOIN_MITER: joinStyle = Qt::MiterJoin; break;
            case S_JOIN_ROUND: joinStyle = Qt::RoundJoin; break;
            default: joinStyle = Qt::RoundJoin;
        }
        d->_pen.setJoinStyle(joinStyle);
        d->_at.joinStyle = attrib->joinStyle;
    }
    d->_pen.setColor(QColor(attrib->color.r, attrib->color.g,
                attrib->color.b, attrib->color.a));
    d->_at.color = attrib->color;
    d->_pen.setWidthF(attrib->width);
    d->_at.width = attrib->width;
}

const SsWireAt* Surface::attribute() const
{
    SH_D(const Surface);
    return &d->_at;
}

void Surface::drawCurve(const SsCurve* curve)
{
    SH_D(Surface);
    if (curve && d->_surface)
    {
        if (curve->n == 1)
            drawPoint(curve->pnts);
        else if (curve->n == 2)
            drawLine(&curve->pnts[0], &curve->pnts[1]);
        else if (curve->n > 3)
        {
            QPainterPath path(QPointF(curve->pnts->x, curve->pnts->y));
            int i = 1;
            while (i < curve->n)
            {
                QPointF c1(curve->pnts[i].x, curve->pnts[i].y);
                ++i;
                QPointF c2(curve->pnts[i].x, curve->pnts[i].y);
                ++i;
                QPointF c3(curve->pnts[i].x, curve->pnts[i].y);
                ++i;
                path.cubicTo(c1, c2, c3);
            }
            QPainter painter(d->_pixmap);
            painter.setRenderHint(QPainter::Antialiasing);
            painter.setPen(d->_pen);
            painter.drawPath(path);
            d->_surface->update();
        }
    }
}


void Surface::drawLine(const SsLine* line)
{
    if (line)
        drawLine(&line->pnt1, &line->pnt2);
}

void Surface::drawLine(const SsPoint* pnt1, const SsPoint* pnt2)
{
    SH_D(Surface);
    if (pnt1 && pnt2 && d->_surface)
    {
        QPainter painter(d->_pixmap);
        painter.setRenderHint(QPainter::Antialiasing);
        painter.setPen(d->_pen);
        painter.drawLine(QPointF(pnt1->x, pnt1->y), QPointF(pnt2->x, pnt2->y));
        d->_surface->update();
    }
}

void Surface::drawPoint(const SsPoint* point)
{
    SH_D(Surface);
    if (point && d->_surface)
    {
        QPainter painter(d->_pixmap);
        painter.setRenderHint(QPainter::Antialiasing);
        painter.setPen(d->_pen);
        painter.drawPoint(QPointF(point->x, point->y));
        d->_surface->update();
    }
}

void Surface::clear()
{
    SH_D(Surface);
    d->_pixmap->fill(Qt::transparent);
}

void Surface::resize(int width, int height)
{
    SH_D(Surface);
    if (width > d->_pixmap->width() ||
        height > d->_pixmap->height())
    {
        QSize size = d->_pixmap->size().expandedTo(QSize(width, height));
        QPixmap* oldPixmap = d->_pixmap;
        d->_pixmap = new QPixmap(size);
        d->_pixmap->fill(Qt::transparent);
        QPainter painter(d->_pixmap);
        painter.drawPixmap(0, 0, *oldPixmap);
        delete oldPixmap;
    }
}

QPixmap* Surface::buffer() const
{
    SH_D(const Surface);
    return d->_pixmap;
}

} // namespace ShUi
